package in.waffl.fairycake;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayDeque;
import java.util.Queue;

public class PollingThread implements Runnable, KeyListener {
	private static final int KEY_COUNT = 256;

	private enum KeyState {
		RELEASED, // Not down
		PRESSED, // Down, but not the first time
		ONCE // Down for the first time
	}

	private boolean[] currentKeys = null;
	private KeyState[] keys = null;
	private Queue<Pollable> pollable = new ArrayDeque<Pollable>();
	private Queue<Pollable> toAdd = new ArrayDeque<Pollable>();
	private Queue<Pollable> toRemove = new ArrayDeque<Pollable>();

	public PollingThread() {
		currentKeys = new boolean[KEY_COUNT];
		keys = new KeyState[KEY_COUNT];
		for (int i = 0; i < KEY_COUNT; ++i) {
			keys[i] = KeyState.RELEASED;
		}
	}

	@Override
	public void run() {
		for (;;) {
			// poll the keyboard
			this.poll();
			for (Pollable k : pollable) {
				k.polled(this);
			}
			while(!toAdd.isEmpty()) {
				pollable.add(toAdd.poll());
			}
			while(!toRemove.isEmpty()) {
				pollable.remove(toRemove.poll());
			}
			try {
				Thread.sleep(1000/Main.FPS);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public synchronized void poll() {
		for (int i = 0; i < KEY_COUNT; ++i) {
			if (currentKeys[i]) {
				if (keys[i] == KeyState.RELEASED)
					keys[i] = KeyState.ONCE;
				else
					keys[i] = KeyState.PRESSED;
			} else {
				keys[i] = KeyState.RELEASED;
			}
		}
	}

	public boolean keyDown(int keyCode) {
		return keys[keyCode] == KeyState.ONCE || keys[keyCode] == KeyState.PRESSED;
	}

	public boolean keyDownOnce(int keyCode) {
		return keys[keyCode] == KeyState.ONCE;
	}

	public synchronized void keyPressed(KeyEvent e) {
		int keyCode = e.getKeyCode();
		if (keyCode >= 0 && keyCode < KEY_COUNT) {
			currentKeys[keyCode] = true;
		}
	}

	public synchronized void keyReleased(KeyEvent e) {
		int keyCode = e.getKeyCode();
		if (keyCode >= 0 && keyCode < KEY_COUNT) {
			currentKeys[keyCode] = false;
		}
	}

	public void keyTyped(KeyEvent e) {
		// Not needed
	}

	public void addPollable(Pollable p) {
		synchronized (toAdd) {
			toAdd.add(p);
		}
	}

	public void removePollable(Pollable p) {
		synchronized (toRemove) {
			toRemove.add(p);
		}
	}
}
